﻿using Cc.Util;
using Magic.Core;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace ManagementTools.Service
{
    /// <summary>
    /// 代码生成器的扩展处理服务
    /// 在代码生成器生成代码之后做一些补充处理
    /// </summary>
    internal class PatchForParentSonService
    {

        public string ErrMsg { get; private set; } = "OK";
        private string mParentName = "";
        private string mSonName = "";
        private string mSrcMainDir = "";
        private string mDstMainDir = "";
        private string mFkShowColumnName = "";

        public PatchForParentSonService(Type parentType, Type sonType,string srcMainDir, string dstMainDir,string fkShowColumnName)
        {
            mParentName = parentType.Name;
            mSonName = sonType.Name;
            mSrcMainDir = srcMainDir;
            mDstMainDir = dstMainDir;
            mFkShowColumnName = fkShowColumnName;
        }
        /// <summary>
        /// 完整的1对多补丁
        /// 会修改:子类的Sevice.cs, manage.js, index.vue add.vue, edit.vue
        /// 父类的index.vue
        /// </summary>
        
        /// <returns></returns>
        public  bool PatchForOneToMulti()
        {
            //先把service目录整体复制过去，修改时会覆盖
            CopyDirectory(Path.Combine(mSrcMainDir, "Service",mParentName), Path.Combine(mDstMainDir, "Service",mParentName));
            CopyDirectory(Path.Combine(mSrcMainDir, "Service", mSonName), Path.Combine(mDstMainDir, "Service", mSonName));
            //parent的js不用修改,拷贝过去
            string src = Path.Combine(mSrcMainDir, "api", "modular", "main", $"{mParentName}Manage.js");
            string dstDir = Path.Combine(mDstMainDir, "api", "modular", "main");
            if(!Directory.Exists(dstDir))Directory.CreateDirectory(dstDir);
            string dst = Path.Combine(dstDir, $"{mParentName}Manage.js");
            File.Copy(src, dst,true);


            src = Path.Combine(mSrcMainDir, "api", "modular", "main", $"{mSonName}Manage.js");
            dstDir = Path.Combine(mDstMainDir, "api", "modular", "main");
             dst = Path.Combine(dstDir, $"{mSonName}Manage.js");
            File.Copy(src, dst, true);
            //parent的addForm.vue, editForm.vue不用修改,拷贝过去
            src = Path.Combine(mSrcMainDir, "views", "main",mParentName, "addForm.vue");
            dstDir = Path.Combine(mDstMainDir, "views", "main", mParentName);
            if (!Directory.Exists(dstDir)) Directory.CreateDirectory(dstDir);
            dst = Path.Combine(dstDir, "addForm.vue");
            File.Copy(src, dst, true);
            src = Path.Combine(mSrcMainDir, "views", "main", mParentName, "editForm.vue");
            dst = Path.Combine(dstDir, "editForm.vue");
            File.Copy(src, dst, true);

            

            //改造后台子类service，IService，父类service,提供关联查询、关联删除服务
            var ans = PacthForOneToMultiService(); //修改service
            if (!ans) return false;
            //改造前台子类js文件，提供关联查询功能
            //ans =  AppendJSCodeForFKQuery();已经通过模板实现，不需改造了
            //if (!ans) return false;

            //改造父类index.vue
            ans = PatchParentIndex();
            if (!ans) return false;
            //改造子类的index.vue
            ans =  PacthSonIndexFile();
            if (!ans) return false;
            //改造子类的AddForm.vue
            ans =  PatchSonAddForm();
            if (!ans) return false;
            //改造子类的editForm.vue
            ans =  PatchSonEditForm();
           
            return ans;
        }
        /// <summary>
        /// 递归调用复制目录及子目录下所有文件
        /// </summary>
        /// <param name="srcPath"></param>
        /// <param name="dstPath"></param>
        private void CopyDirectory(string srcPath, string dstPath)
        {
            foreach (var file in Directory.GetFiles( srcPath))
            {
                if(!Directory.Exists(dstPath))Directory.CreateDirectory(dstPath);
                var dst = file.Replace(srcPath, dstPath);
                File.Copy(file,dst, true);
                
            }
            foreach (var dir in Directory.GetDirectories(srcPath))
            {
                var name=Path.GetFileNameWithoutExtension(dir);
                var dstDir = Path.Combine(dstPath, name);
                if(!Directory.Exists(dstDir))Directory.CreateDirectory(dstDir);
                CopyDirectory(dir, dstDir);
            }
        }
        private bool  PatchSonEditForm()
        {
            string sFile = Path.Combine(mSrcMainDir, "views", "main", mSonName, "editForm.vue");
            if (!File.Exists(sFile))
            {
                sFile = Path.Combine(mSrcMainDir, "views", "main", mParentName, mSonName, "editForm.vue");
            }
            if (!File.Exists(sFile))
            {
                ErrMsg = $"缺少{mSonName}\\editForm.vue 文件";
                return false;
            }
            string targFile = Path.Combine(mDstMainDir,"views","main", mParentName,mSonName,"editForm.vue");
            var lines = File.ReadAllLines(sFile).ToList();
            if (!lines.Any() || lines.Count < 4)
            {
                ErrMsg = $"{sFile}内容为空或者有误";
                return false;
            }
            //父实例Id和父实例的显示字段在summit方法中处理，无需输入，不显示
            for (int i = 0; i < lines.Count; i++)
            {
                if (lines[i].Contains("a-form-item") && lines[i].Contains(mParentName))
                {
                    lines[i] = lines[i].Replace("a-form-item", "a-form-item v-if=\"false\"");
                }
            }
            // 暂存传入的父级对象，以备提交时用
            int index = lines.FindIndex(ln => ln.Trim() == "confirmLoading: false,");
            if (index == -1)
            {
                ErrMsg = "未找到特征字串: confirmLoading: false,";
                return false;
            }
            lines.Insert(index + 1, "         parent:[], // 暂存传入的父级对象，以备提交时用");
            index = lines.FindIndex(ln => ln.Trim() == "edit (record) {");
            if (index == -1)
            {
                ErrMsg = "未找到特征字串: edit (record)";
                return false;
            }
            lines.Insert(index + 1, "        this.parent = record  //暂存传入的父级对象，以备提交时用");
            //提交表单，添加关联的父级对象的Id及显示字段
            index = lines.FindIndex(ln => ln.Trim().StartsWith("validateFields((errors, values)"));
            if (index == -1)
            {
                ErrMsg = "未找到特征字串: validateFields((errors, values)";
                return false;
            }
            lines.Insert(index + 1, $"          values['{mParentName.LowerFirstCharacter()}Id']=this.parent.{mParentName.LowerFirstCharacter()}Id");
            lines.Insert(index + 2, $"          values['{mFkShowColumnName.LowerFirstCharacter()}Id']=this.parent.{mFkShowColumnName.LowerFirstCharacter()}Id");
             File.WriteAllLines(targFile, lines);
            return true;
        }
        /// <summary>
        /// 
        /// </summary>
       
        /// <returns></returns>
        private  bool PatchSonAddForm()
        {
            string sFile = Path.Combine(mSrcMainDir,"views","main", mSonName, "addForm.vue");
            if (!File.Exists(sFile))
            {
                sFile = Path.Combine(mSrcMainDir, "views", "main", mParentName, mSonName, "addForm.vue");
            }
            if (!File.Exists(sFile))
            {
                ErrMsg = $"缺少..{mSonName}\\addForm.vue 文件";
                return false;
            }
            string targFile = Path.Combine(mDstMainDir, "views", "main", mParentName, mSonName, "addForm.vue");
            var lines = File.ReadAllLines(sFile).ToList();
            if (!lines.Any() || lines.Count < 4)
            {
                ErrMsg = $"{sFile}内容为空或者有误";
                return false;
            }
            //父实例Id和父实例的显示字段在summit方法中处理，无需输入，不显示
            for (int i = 0; i < lines.Count; i++)
            {
                if(lines[i].Contains("a-form-item") && lines[i].Contains(mParentName))
                {
                    lines[i] = lines[i].Replace("a-form-item", "a-form-item v-if=\"false\"");
                }
            }
            // 暂存传入的父级对象，以备提交时用
            int index = lines.FindIndex(ln => ln.Trim() == "confirmLoading: false,");
            if (index == -1)
            {
                ErrMsg = "未找到特征字串";
                return false;
            }
            lines.Insert(index+1, "         parent:[], // 暂存传入的父级对象，以备提交时用");
            index = lines.FindIndex(ln => ln.Trim() == "add (record) {");
            if (index == -1)
            {
                ErrMsg = "未找到特征字串";
                return false;
            }
            lines.Insert(index + 1, "        this.parent = record  //暂存传入的父级对象，以备提交时用");
            //提交表单，添加关联的父级对象的Id及显示字段
            index = lines.FindIndex(ln => ln.Trim().StartsWith("validateFields((errors, values)"));
            if(index == -1)
            {
                ErrMsg = "未找到特征字串";
                return false;
            }
            lines.Insert(index + 1, $"        values['{mParentName.LowerFirstCharacter()}Id']=this.parent.{mParentName.LowerFirstCharacter()}Id");
            lines.Insert(index + 2, $"        values['{mFkShowColumnName.LowerFirstCharacter()}Id']=this.parent.{mFkShowColumnName.LowerFirstCharacter()}Id");
            string dir = Path.GetDirectoryName(targFile);
            if (!File.Exists(dir)) Directory.CreateDirectory(dir);
             File.WriteAllLines(targFile, lines);
            return true;
        }
        private  bool  PacthSonIndexFile()
        {
            string sFile = Path.Combine(mSrcMainDir, "views", "main", mSonName, "index.vue");
            if (!File.Exists(sFile))
            {
                sFile = Path.Combine(mSrcMainDir, "views", "main", mParentName, mSonName, "index.vue");
            }
            if (!File.Exists(sFile))
            {
                ErrMsg = $"缺少{mSonName}\\index.vue 文件";
                return false;
            }
            string targFile = Path.Combine(mDstMainDir, "views", "main", mParentName, mSonName, "index.vue");
            var lines = File.ReadAllLines(sFile).ToList();
            if (!lines.Any() || lines.Count<4)
            {
                ErrMsg = $"{sFile}内容为空或者有误";
                return false;
            }
            
            //修改为a-modal模式
            lines[1] = $"<a-modal  title=\"{mSonName}管理\"  :width=\"900\"  :visible=\"visible\"  :footer=\"null\"  @@cancel=\"handleCancel\">";
            int index = lines.FindIndex(it => it.Trim() == "<script>");
            if (index == -1)
            {
                ErrMsg = "未找到特征字串:<script>";
                return false;
            }
            lines[index - 2] = "</a-modal>";

            //加入x-card
            lines.Insert(2, $"  <x-card v-if=\"hasPerm('{mSonName}:page')\">");

            index = lines.FindIndex(it => it.Trim() == "</a-card>");//有两个a-card，插入到第一个a-card后
            if (index == -1)
            {
                ErrMsg = "未找到特征字串:</a-card>";
                return false;
            }
            lines.Insert(index + 1, "  </x-card>");
            index = lines.FindIndex(it => it.Trim() == "<script>");
            lines[index + 1] = "  import { STable,XCard } from '@/components'";
            lines.Insert(index + 8, "      XCard,   //用到XCard控件");

            //修改查询方法，执行关联查询
            lines[index + 2] = $"  import {{ {mSonName}Page,{mSonName}Delete,{mSonName}Fk{mParentName}List }} from '@/api/modular/main/{mSonName}Manage' //导入级联查询方法";
            index = lines.FindIndex(it => it.Trim() == "loadData: parameter => {");
            if (index == -1)
            {
                ErrMsg = "未找到特征字串: loadData: parameter => {";
                return false;
            }
            lines[index + 1] = $"            return {mSonName}Fk{mParentName}List(Object.assign(parameter, this.queryParam)).then((res) => {{ //修改查询方法，执行关联查询";

            //记录导入的父级实例并新增handleCancel方法
            index = lines.FindIndex(it => it.Trim() == "selectedRows: []");
            if (index == -1)
            {
                ErrMsg = "未找到特征字串: selectedRows: []";
                return false;
            }
            lines.Insert(index, "        record:[],");
            lines.Insert(index, "        visible:false,");
            index = lines.FindIndex(it => it.Trim() == "methods: {");
            if (index == -1)
            {
                ErrMsg = "未找到特征字串: methods: {";
                return false;
            }
            string[] codes = new string[]
            {
                "       index (record) {",
                "           this.visible = true",
                "           this.record = record",
                $"          this.queryParam.{mParentName}Id = record.id",
                "           try {",
                "               this.$refs.table.refresh()",
                "           } catch (e) {",
                "             // 首次进入界面，因表格加载顺序，会抛异常，我们不予理会",
                "       }},",
                "       handleCancel(){",
                "           this.queryParam={};",
                "           this.visible=false;",
                "       },"
            };
            lines.InsertRange(index+1,codes);

            //addForm传参record,以便在保存时传入父级信息
            index = lines.FindIndex(it => it.Contains("refs.addForm.add()"));
            if (index == -1)
            {
                ErrMsg = "未找到特征字串: refs.addForm.add()";
                return false;
            }
            lines[index]=lines[index].Replace("refs.addForm.add()", "refs.addForm.add(record)");
            string dir = Path.GetDirectoryName(targFile);
            if (!File.Exists(dir)) Directory.CreateDirectory(dir);
             File.WriteAllLines(targFile, lines);

            return true;
        }
        /// <summary>
        /// 为服务增加处理1对多表的关联查询以及关联删除功能
        /// 约定：子类的外键名为父类名+Id，指向父类的Id字段,如Ret.RcuId，指向Rcu.Id
        /// </summary>
        /// <typeparam name="TParent"></typeparam>
        /// <typeparam name="TSon"></typeparam>
        /// <param name="mSrcMainDir"></param>
        /// <returns></returns>
        private   bool  PacthForOneToMultiService()
        {
            string file = Path.Combine(mSrcMainDir, "service",mSonName, $"{mSonName}Service.cs");
            string ifile = Path.Combine(mSrcMainDir, "service", mSonName, $"I{mSonName}Service.cs");
            string pfile = Path.Combine(mSrcMainDir, "service", mParentName, $"{mParentName}Service.cs");
            //string file = SearchFile(mSrcMainDir, $"{mSonName}Service.cs");//子类服务
            //string ifile = SearchFile(mSrcMainDir, $"I{mSonName}Service.cs");//子类服务接口
            //string pfile = SearchFile(mSrcMainDir, $"{mParentName}Service.cs");//父类服务
            
            if(string.IsNullOrEmpty(ifile)||string.IsNullOrEmpty(file) || string.IsNullOrEmpty(pfile))
            {
                ErrMsg = $"未找到{mSonName}Service.cs或I{mSonName}Service.cs";
                return false;
            }
            List<string> codes = new List<string>
            {
                "       /// <summary>",
                $"       /// 获取{mParentName}的{mSonName}集合",
                "       /// </summary>",
                "       /// <param name=\"input\"></param>",
                "       ///  <returns></returns>",
                $"       [HttpGet(\"/{mSonName}/fk{mParentName}\")]",
                $"       public async Task<dynamic> Get{mSonName}ListBy{mParentName}([FromQuery] {mSonName}Input input)",
                "       {",
                $"           var entities = await _rep.Context.Queryable<{mSonName}>().Where(r=>r.{mParentName}Id==input.{mParentName}Id)",
                $"               .ToPagedListAsync(input.PageNo, input.PageSize);",
                "           return entities.XnPagedResult();",
                "       }",

                $"       public async  Task DeleteBy{mParentName}(long id)  ",
                "       {  ",
                $"          foreach (var entity in _rep.ToList(u => u.{mParentName}Id == id))  ",
                "          {",
                "              await _rep.DeleteAsync(entity);",
                "           }",
                "       }   ",
            };
           
            string outFile = Path.Combine(mDstMainDir, "service" ,mSonName, $"{mSonName}Service.cs");
            var  ans =  AppendCSCode(file, codes, outFile);
            if (ans)
            {
                codes = new List<string>
                {
                    $"     Task<dynamic> Get{mSonName}ListBy{mParentName}([FromQuery] {mSonName}Input input);",
                    $"     Task DeleteBy{mParentName}(long parentId);  ",
                };
                outFile=  Path.Combine(mDstMainDir, "service", mSonName, $"I{mSonName}Service.cs");
                ans =    AppendCSCode(ifile, codes,outFile);
                if (ans)
                {
                    outFile = Path.Combine(mDstMainDir, "service", mParentName, $"{mParentName}Service.cs");
                    return  PatchParentServiceFile(pfile, outFile);
                }
            }
            else
            {
                return false;
            }

            return true;
        }

        private  bool  PatchParentServiceFile(string pfile, string outfile)
        {
            //读入旧代码
            List<string>lines=File.ReadAllLines(pfile).ToList();
            //注入子类服务
            int index = lines.FindIndex(it => it.Contains($"SqlSugarRepository<{mParentName}> _rep;")); //找到行private readonly SqlSugarRepository<DemoRcu> _rep;
            if (index == -1)
            {
                ErrMsg = "未找到特征字串";
                return false;
            }
            lines.Insert(index+1, $"        private I{mSonName}Service m{mSonName}Service;"); //插入子类service成员            

            index=lines.FindIndex(it => it.Contains($"SqlSugarRepository<{mParentName}> rep")); //找到构造函数行
            lines[index]=lines[index].Replace(")", $", I{mSonName}Service @{mSonName}Service)");//注入子类服务
           
            index = lines.FindIndex(it => it.Contains("_rep = rep;")); //找到行_rep = rep;
            if (index == -1)
            {
                ErrMsg = "未找到特征字串";
                return false;
            }
            lines.Insert(index + 1, $"           m{mSonName}Service = @{mSonName}Service;"); //接收子类service传参

            index = lines.FindIndex(it => it.Contains($"Delete(Delete{mParentName}Input input)")); //定位删除父实体函数
            if (index == -1)
            {
                ErrMsg = "未找到特征字串";
                return false;
            }
            lines.Insert(index + 2, $"               m{mSonName}Service.DeleteBy{mParentName}(input.Id);//删除父实体下所有子实体");
            string dir = Path.GetDirectoryName(outfile);
            if (!File.Exists(dir)) Directory.CreateDirectory(dir);
             File.WriteAllLines(outfile, lines);
            return true;
        }

        /// <summary>
        /// 在CS文件的最后两行处（相当于回退两个}号）追加代码行
        /// </summary>
        /// <param name="orgFile"></param>
        /// <param name="codes"></param>
        /// <param name="targFile"></param>
        /// <returns></returns>
        private   bool  AppendCSCode(string orgFile,IEnumerable< string> codes,string targFile)
        {
            var lines = File.ReadAllLines(orgFile).ToList();
            string dir=Path.GetDirectoryName(targFile);
            if(!File.Exists(dir))Directory.CreateDirectory(dir);
            if(lines.Count > 2)
            {
                int index = lines.FindLastIndex(ln => ln.Trim().StartsWith("}"));//定位到Task Update行
                if(index == -1)
                {
                    ErrMsg = "找不到特征行：}";
                    return false;
                }
                lines.InsertRange(index-2, codes);
                 File.WriteAllLines(targFile, lines);
                return true;
            }
            else
            {
                ErrMsg = $"读取文件{orgFile}错误，不是CS源代码文件？";
                return false;
            }
        }
        /// <summary>
        /// 为子类的JS追加关联查询功能
        /// </summary>
        /// <typeparam name="TParent"></typeparam>
        /// <typeparam name="TSon"></typeparam>
        /// <param name="parent"></param>
        /// <param name="son"></param>
        /// <returns></returns>
        private  bool  AppendJSCodeForFKQuery()
        {
            string file = Path.Combine(mSrcMainDir, "api", "modular", "main", $"{mSonName}Manage.js");
            var lines=File.ReadAllLines(file).ToList();
           // string file = SearchFile(mSrcMainDir, $"{mSonName}Manage.js");
            List<string> codes = new List<string>
            {
                "/**                                                                  ",
                $" * 查询{mParentName}的{mSonName}清单                                         ",
                " *                                                                   ",
                " * @author Ccjungle                                                  ",
                " */                                                                  ",
                " export function "+mSonName+"Fk"+mParentName+"List (parameter) {              ",
                "  return axios({                                                     ",
                $"    url: '/{mSonName}/fk{mParentName}',                                       ",
                "    method: 'get',                                                   ",
                "    params: parameter                                                ",
                "  })                                                                 ",
                "}                                                                    ",
            };
            lines.AddRange(codes);
            string outFile = Path.Combine(mDstMainDir, "api","modular","main", $"{mSonName}Manage.js");
            string dir = Path.GetDirectoryName(outFile);
            if (!File.Exists(dir)) Directory.CreateDirectory(dir);
             File.WriteAllLines(outFile, lines);
            return true;
        }
       


        /// <summary>
        /// 在指定的目录及子目录下查找文件
        /// </summary>
        /// <param name="directory"></param>
        /// <param name="filename"></param>
        /// <returns></returns>
        private static string SearchFile(string directory, string filename)
        {
            var files=Directory.GetFiles(directory);
            if(files.Contains(filename)) return Path.Combine(directory, filename);
            var dirs=Directory.GetDirectories(directory);
            foreach(var dir in dirs)
            {
                return SearchFile(dir, filename);
            }
            return "";
        }
        /// <summary>
        /// 往父类添加子类的组件链接，支持一个父类多子类
        /// 每调用一次，多一个子类的链接
        /// 组件存放路径./parent/son/index.vue
        /// </summary>
        /// <param name="parentIndexFile"></param>
        private  bool  PatchParentIndex()
        {
            /*
             * 1. 在编辑、删除前添加指向son/index的a标签
             * 2. 导入parent/sun/index.vue
             */
            var parentIndexFile = Path.Combine(mSrcMainDir,"views","main", mParentName,"index.vue");
            if(!File.Exists(parentIndexFile))
            {
                ErrMsg = $"源文件{parentIndexFile}不存在！";
                return false;
            }
            var lines = File.ReadAllLines(parentIndexFile).ToList();
            if(!lines.Any()) 
            {
                ErrMsg = $"{parentIndexFile}内容为空";
                return false;
            }


            //添加到子son/index.vue组件的a标签
            int index = lines.FindIndex(ln => ln.Contains($"hasPerm('{mParentName}:edit')") && ln.Contains("$refs.editForm.edit(record)"));
            if(index == -1)
            {
                ErrMsg = "找不到特征行: @click=\"$refs.editForm.edit(record)\"";
                return false;
            }
            lines.Insert(index+1,$"          <a-divider type=\"vertical\" v-if=\"hasPerm('{mParentName}:edit') & hasPerm('{mSonName}:page')\"/>");

            //插入入<son-index ref="retIndex" @ok="handleOk" />
            index = lines.FindIndex(ln => ln.Contains("<edit-form ref=\"editForm\"") && ln.Contains("@ok=\"handleOk\""));
            if(index == -1)
            {
                ErrMsg = "找不到特征行: <edit-form ref=\"editForm\" @ok=\"handleOk\" />";
                return false;
            }
            lines.Insert(index + 1, $"      <{mSonName.LowerFirstCharacter()}-index\" ref=\"{mSonName.LowerFirstCharacter()}Index\" @ok=\"handleOk\">");

            //导入son/index.vue
            index = lines.FindIndex(ln =>  ln.Contains("import editForm"));
            if (index == -1)
            {
                ErrMsg = "找不到特征行:  import editForm from './editForm.vue'";
                return false;
            }
            lines.Insert(index + 1, $"  import {mSonName.LowerFirstCharacter()}Index from './{mSonName}/index.vue'");//子级组件放在/父/子目录下

            //export default component sonIndex
            index = lines.FindIndex(ln => ln.EndsWith("editForm"));
            if (index == -1)
            {
                ErrMsg = "找不到特征行:   editForm ";
                return false;
            }
            lines[index] = lines[index] + ",";
            lines.Insert(index + 1, $"      {mSonName.LowerFirstCharacter()}Index");
            var targParentIndexFile = Path.Combine(mDstMainDir, "views", "main", mParentName, "index.vue");
            string dir = Path.GetDirectoryName(targParentIndexFile);
            if (!File.Exists(dir)) Directory.CreateDirectory(dir);
             File.WriteAllLines(targParentIndexFile, lines);
            return true; 
        }
       
    }
}
